package cn.modoumama.notice.util;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Set;

import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.util.ClassUtils;

import cn.modoumama.common.spring.filter.AnnotationMethodFilter;
import cn.modoumama.common.utils.StringUtils;
import cn.modoumama.notice.annotation.NoticeApi;
import cn.modoumama.notice.entity.ApiMethodCfgModel;
import cn.modoumama.notice.entity.ApiMethodModel;

public class ClassPathNoticeApiServiceScanner extends ClassPathBeanDefinitionScanner {
	private Class<? extends Annotation> typeAnnotationType;
	private Class<? extends Annotation> methodAnnotationType = NoticeApi.class;
	private String baseUrl = null;
	private String httpInvokerRequestExecutorBeanName;

	public Class<? extends Annotation> getTypeAnnotationType() {
		return typeAnnotationType;
	}

	public void setTypeAnnotationType(Class<? extends Annotation> typeAnnotationType) {
		this.typeAnnotationType = typeAnnotationType;
	}

	public Class<? extends Annotation> getMethodAnnotationType() {
		return methodAnnotationType;
	}

	public void setMethodAnnotationType(Class<? extends Annotation> methodAnnotationType) {
		this.methodAnnotationType = methodAnnotationType;
	}

	public final String getBaseUrl() {
		return this.baseUrl;
	}

	public final void setBaseUrl(String baseUrl) {
		this.baseUrl = baseUrl;
	}

	public final String getHttpInvokerRequestExecutorBeanName() {
		return this.httpInvokerRequestExecutorBeanName;
	}

	public final void setHttpInvokerRequestExecutorBeanName(String httpInvokerRequestExecutorBeanName) {
		this.httpInvokerRequestExecutorBeanName = httpInvokerRequestExecutorBeanName;
	}

	public ClassPathNoticeApiServiceScanner(BeanDefinitionRegistry registry) {
		super(registry, false);
	}

	public void registerFilters() {
		if (this.typeAnnotationType != null || this.methodAnnotationType != null) {
			addIncludeFilter(new AnnotationMethodFilter(this.typeAnnotationType, this.methodAnnotationType, true));
		}

		addExcludeFilter(new TypeFilter() {
			public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
					throws IOException {
				String className = metadataReader.getClassMetadata().getClassName();
				return className.endsWith("package-info");
			}
		});
	}

	@SuppressWarnings("unchecked")
	public Set<BeanDefinitionHolder> doScan(String... basePackages) {
		Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
		for (BeanDefinitionHolder holder : beanDefinitions) {
			GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();
			if (this.logger.isDebugEnabled()) {
				this.logger.debug("Creating HttpInvokerProxyFactoryBean with name '" + holder.getBeanName() + "' and '"
						+ definition.getBeanClassName() + "' serviceInterface");
			}

			try {
				String ver = "1.0";
				//如果是接口且有RemoteService注解，直接设置
				Class<?> clazz = ClassUtils.forName(definition.getBeanClassName(), ClassUtils.getDefaultClassLoader());
				Class<?> serviceInterface = null;
				Class<? extends Annotation> remoteService = (Class<? extends Annotation>) ClassUtils.forName("org.springframework.remoting.service.annotation.RemoteService", ClassUtils.getDefaultClassLoader());
				if (AnnotationUtils.isAnnotationDeclaredLocally(remoteService, clazz)) {
					if(clazz.isInterface()){
						serviceInterface = clazz;
					}
				}

				//判断类的注解上是否有spring 的 Service注解，实现接口有RemoteService注解
				if (serviceInterface == null) {
					try {
						Class<? extends Annotation> springService = (Class<? extends Annotation>) ClassUtils
								.forName("org.springframework.stereotype.Service", ClassUtils.getDefaultClassLoader());
						if (AnnotationUtils.isAnnotationDeclaredLocally(springService, clazz)) {
							Class<?>[] interfaceClasses = clazz.getInterfaces();
							if (typeAnnotationType == null) {
								for (int i = 0; i < interfaceClasses.length; i++) {
									Class<?> class1 = interfaceClasses[i];
									if (AnnotationUtils.isAnnotationDeclaredLocally(remoteService, class1)) {
										serviceInterface = class1;
										break;
									}
								}
								
							} else {
								for (Class<?> interfaceClass : interfaceClasses) {
									if (typeAnnotationType != null && AnnotationUtils.isAnnotationDeclaredLocally(typeAnnotationType, interfaceClass)) {
										serviceInterface = interfaceClass;
										ver = "1.0";
										break;
									}
								}
							}

						}
					} catch (LinkageError e) {
					}
				}
				
				//判断类的注解上是否有dubbo 的 Service注解
				if (serviceInterface == null) {
					try {
						Class<? extends Annotation> dubboService = (Class<? extends Annotation>) ClassUtils.forName(
								"com.alibaba.dubbo.config.annotation.Service", ClassUtils.getDefaultClassLoader());
						Class<?>[] interfaceClasses = clazz.getInterfaces();
						if (AnnotationUtils.isAnnotationDeclaredLocally(dubboService, clazz)) {
							Annotation services = clazz.getAnnotation(dubboService);
							Method version = dubboService.getMethod("version");
							ver = (String) version.invoke(services);
							if (typeAnnotationType == null) {
								serviceInterface = interfaceClasses[0];
							} else {
								for (Class<?> interfaceClass : interfaceClasses) {
									if (typeAnnotationType != null && AnnotationUtils
											.isAnnotationDeclaredLocally(typeAnnotationType, interfaceClass)) {
										serviceInterface = interfaceClass;
										break;
									}
								}
							}
						}
					} catch (LinkageError e) {
					}
				}

				//添加接口信息到数据库
				if (serviceInterface != null) {
					definition.getPropertyValues().add("serviceInterface", serviceInterface);

					Method[] methods = clazz.getMethods();
					NoticeApi noticeApi = null;
					for (Method method : methods) {

						noticeApi = (NoticeApi) method.getAnnotation(NoticeApi.class);

						if (noticeApi != null) {
							ApiMethodModel apiMethodModel = ApiMethodUtils.getApiMethod(noticeApi.method());
							if (apiMethodModel == null) {
								apiMethodModel = new ApiMethodModel();
								apiMethodModel.setMethod(noticeApi.method());
								ApiMethodUtils.addApiMethod(apiMethodModel);
							}
							apiMethodModel.setAuthLevel(noticeApi.authLevel());
							apiMethodModel.setInvokeDayMaxNum(noticeApi.invokeDayMaxNum());
							apiMethodModel.setInvokeMinMaxNum(noticeApi.invokeMinMaxNum());
							apiMethodModel.setIsExtras(noticeApi.isExtras());
							apiMethodModel.setIsOpen(noticeApi.isOpen());
							apiMethodModel.setIsUpdated(noticeApi.isUpdated());
							apiMethodModel.setMethodName(noticeApi.methodName());
							apiMethodModel.setMethodMemo(noticeApi.methodMemo());
							apiMethodModel.setMethodType(noticeApi.methodType());

							//添加接口详细配置信息
							ApiMethodCfgModel apiMethodCfgModel = new ApiMethodCfgModel();
							StringBuffer cfgValue = new StringBuffer(
									serviceInterface.getName() + ";" + method.getName());
							if (method.getParameterTypes() != null && method.getParameterTypes().length > 0) {
								cfgValue.append(";" + method.getParameterTypes()[0].getName());
							}
							apiMethodCfgModel.setBaseUrl(baseUrl);
							apiMethodCfgModel.setCfgValue(cfgValue.toString());
							if(StringUtils.isNotBlank(noticeApi.ver())){
								ver = noticeApi.ver();
							}
							apiMethodCfgModel.setVer(ver);
							apiMethodModel.addApiMethodCfg(apiMethodCfgModel);
						}
					}
				}
			} catch (Exception e) {
				this.logger.error(e.getMessage(), e);
			}
			definition.setAutowireMode(2);
		}
		return beanDefinitions;
	}

	protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
		return true;
	}

	protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {
		return true;
	}
}